package cn.zebra.dev.net.local.utils;

import cn.zebra.dev.common.thread.utils.ThreadUtils;
import cn.zebra.dev.net.prot.Port;
import cn.zebra.dev.net.prot.PortTask;

import java.net.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.stream.Collectors;

/**
 * 本地网络工具
 *
 * @author runnable@sina.cn
 */
public class LocalNetUtils {

    private static InetAddress inetAddress;

    private static Enumeration<NetworkInterface> networkInterfaces;

    /**
     * 测试本机端口是否被使用
     *
     * @param port 端口
     * @return 是否被使用
     */
    public static boolean isLocalPortUsing(int port) throws ExecutionException, InterruptedException {
        PortTask portTask = new PortTask("127.0.0.1", port);
        FutureTask<Port> target = new FutureTask<>(portTask);
        new Thread(target).start();
        Port task = target.get();
        return task.getFlag();
    }

    static {
        try {
            inetAddress = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

    static {
        try {
            networkInterfaces = NetworkInterface.getNetworkInterfaces();
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取本机名称
     *
     * @return 本机名称
     */
    public static String getHostName() {
        return inetAddress.getHostName();
    }

    /**
     * 获取本地本机地址
     *
     * @return 本机地址
     */
    public static String getHostAddress() {
        return inetAddress.getHostAddress();
    }

    /**
     * 获取本地本机ip
     *
     * @return ip
     */
    public static String getLocalIp() {
        return getHostAddress();
    }

    /**
     * 获取所有IPv4的IP地址
     *
     * @return 所有IPv4的IP地址
     */
    public static List<String> getLocalIps() {
        List<String> ipList = new ArrayList<>();
        NetworkInterface networkInterface;
        Enumeration<InetAddress> inetAddresses;
        InetAddress inetAddress;
        String ip;
        while (networkInterfaces.hasMoreElements()) {
            networkInterface = networkInterfaces.nextElement();
            inetAddresses = networkInterface.getInetAddresses();
            while (inetAddresses.hasMoreElements()) {
                inetAddress = inetAddresses.nextElement();
                // IPV4
                if (inetAddress instanceof Inet4Address) {
                    ip = inetAddress.getHostAddress();
                    ipList.add(ip);
                }
            }
        }
        return ipList;
    }

    /**
     * 获取本机所有被使用的端口
     *
     * @return 端口集合
     */
    public static Collection<Port> getLocalPortIsUse() {
        Collection<Port> ports = getAllLocalPort();
        return ports.stream().filter(Port::getFlag).collect(Collectors.toList());
    }

    /**
     * 获取本机所有未使用的端口
     *
     * @return 端口集合
     */
    public static Collection<Port> getLocalPortNotUse() {
        Collection<Port> ports = getAllLocalPort();
        return ports.stream().filter(port -> !port.getFlag()).collect(Collectors.toList());
    }

    /**
     * 获取本机所有端口信息
     *
     * @return 端口集合
     */
    public static Collection<Port> getAllLocalPort() {
        int cpu = ThreadUtils.getAvailableProcessors();
        int start = 0;
        int end = 65535;
        int initialCapacity = end - start;
        List<FutureTask<Port>> futureTasks = new ArrayList<>(initialCapacity);
        // 预启动所有核心线程
        ExecutorService executorService = Executors.newFixedThreadPool(cpu * 164);

        for (int i = start; i <= end; i++) {
            PortTask portTask = new PortTask("127.0.0.1", i);
            FutureTask<Port> target = new FutureTask<>(portTask);
            executorService.execute(target);
            futureTasks.add(target);
        }
        //关闭线程池
        executorService.shutdown();
        List<Port> ports = new ArrayList<>(initialCapacity);
        futureTasks.forEach(task -> {
            Port portEntity = null;
            try {
                portEntity = task.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
            if (portEntity != null) {
                ports.add(portEntity);
            }
        });
        return ports;
    }

    public static Enumeration<NetworkInterface> getNetworkInterfaces() {
        return networkInterfaces;
    }

    public static InetAddress getINetAddress() {
        return inetAddress;
    }
}
